﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using Asol.Reporting.Support;
using System.Drawing.Drawing2D;

namespace Asol.Reporting.Green.Components
{
    /// <summary>
    /// Painter: maluje komponenty
    /// </summary>
    public class Painter
    {
        #region poznámky ke kreslení ve WinForm, s přihlédnutím k práci se souřadnicemi a rozměry
        /*

        Pokud si pracovní plochu přirovnáme k milimetrovému papíru, pak horní řádek má souřadnici Y = 0, a levý sloupec má souřadnici X = 0.
        Pokud na tento milimetrový papír budeme kreslit Rectangle(10, 10, 30, 20), pak jím pokreslené pixely budou mít nasledující souřadnice:
        graphics.DrawRectangle(pen, area);    pokud pen má šířku 1px, pak budou obarveny pixely X: 10 a 40, Y: 10 a 30, tedy šířka pokresleného prostoru bude 31 a výška 21.
        graphics.FillRectangle(brush, area);  budou pokresleny pixely X: 10 až 39; Y: 10 až 29, tedy šířka pokresleného prostoru bude 30 a výška 20.

        Pokud vytvořím GraphicPath za pomoci bodů v souřadnicích daného Rectangle (s využitím hodnot X a Right, a Y a Bottom), a následně takovou Path vykreslím:
        graphics.DrawPath(pen, path);         pokud pen má šířku 1px, pak budou obarveny body shodně jako u graphics.DrawRectangle(), 
                                                protože souřadnice Right = 40 (10 + 30) a Bottom = 30 (10 + 20), zabere se tedy opět prostor šířky 31 a výšky 21.

        Obecně lze tedy říci, že Draw probíhá přesně po daných souřadnicích (X, Right), a tím že obsadí i souřadnici Right, tak obsazuje šířku +1 (+PenWidth).
        Kreslení Draw pohybuje perem po dané souřadnici, a pero přesahuje doprava a dolů o svoji šířku. 
        Naproti tomu Fill začíná na souřadnici X, a končí před souřadnicí Right, protože Fill naplní daný počet pixelů (Width).

        Závěry:
        -  pokud chci vykreslit rámeček široký 40 pixelů, a mám pero šířky 1 pixel, pak Width musí být (40 - 1) = 39.
        -  pokud chystám výplň do daného Rectangle, pak musím X a Y posunout o 1 pixel (nebo o šířku pera), a výplň musí mít šířku a výšku o 1 pixel menší než vykreslený Rectangle.

        */
        #endregion
        #region DrawRadiance
        public static void DrawRadiance(Graphics graphics, Point center, Color centerColor)
        {
            DrawRadiance(graphics, center, null, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Point center, Rectangle? clipBounds, Color centerColor)
        {
            Rectangle bounds = center.CreateRectangleFromCenter(new Size(45, 30));
            DrawRadiance(graphics, bounds, clipBounds, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Point center, Size size, Color centerColor)
        {
            DrawRadiance(graphics, center, size, null, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Point center, Size size, Rectangle? clipBounds, Color centerColor)
        {
            Rectangle bounds = center.CreateRectangleFromCenter(size);
            DrawRadiance(graphics, bounds, clipBounds, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Rectangle bounds, Color centerColor)
        {
            DrawRadiance(graphics, bounds, null, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Rectangle bounds, Rectangle? clipBounds, Color centerColor)
        {
            System.Drawing.Drawing2D.GraphicsPath p = new System.Drawing.Drawing2D.GraphicsPath();
            p.AddEllipse(bounds);
            using (System.Drawing.Drawing2D.PathGradientBrush b = new System.Drawing.Drawing2D.PathGradientBrush(p))
            {
                b.CenterColor = centerColor;
                b.CenterPoint = bounds.Center();
                b.SurroundColors = new Color[] { Color.Transparent };
                if (clipBounds.HasValue)
                {
                    Region clip = graphics.Clip;
                    graphics.SetClip(clipBounds.Value);
                    graphics.FillEllipse(b, bounds);
                    graphics.Clip = clip;
                }
                else
                {
                    graphics.FillEllipse(b, bounds);
                }
            }
        }
        #endregion
        #region PaintButton
        public static void PaintButton(IPaintBackground source, Graphics graphics)
        {
            PaintState state = source.State;
            if (state == PaintState.None) return;
            bool isDisabled = ((state & PaintState.Disabled) != 0);
            bool isStandard = ((state & PaintState.Standard) != 0);
            bool isHotMouse = ((state & PaintState.HotMouse) != 0);
            bool isFocused = ((state & PaintState.Focused) != 0);
            bool isPressed = ((state & PaintState.Pressed) != 0);

            if (isDisabled)
                PaintButtonDisabled(source, graphics);
            else if (isPressed)
                PaintButtonPressed(source, graphics);
            else if (isHotMouse && isFocused)
                PaintButtonHotFocus(source, graphics);
            else if (isFocused)
                PaintButtonFocused(source, graphics);
            else if (isHotMouse)
                PaintButtonHotMouse(source, graphics);
            else
                PaintButtonStandard(source, graphics);
        }
        public static void PaintButtonDisabled(IPaintBackground source, Graphics graphics)
        {
            Color color1, color2; float angle;
            PaintGetColors(source.BackColorDisabled, PaintState.Disabled, out color1, out color2, out angle);
            PaintBackground(source.Bounds, graphics, 0, color1, color2, angle);
        }
        public static void PaintButtonPressed(IPaintBackground source, Graphics graphics)
        {
            Color color1, color2; float angle;
            PaintGetColors(source.BackColorPressed, PaintState.Pressed, out color1, out color2, out angle);
            PaintBackground(source.Bounds, graphics, 0, color1, color2, angle);
            PaintBackground(source.Bounds, graphics, 3, color1, color2, angle - 180f);
            PaintBorder(source, graphics, source.BorderColor, 0, 1, 2, System.Drawing.Drawing2D.DashStyle.Solid);
        }
        public static void PaintButtonHotFocus(IPaintBackground source, Graphics graphics)
        {
            Color color = source.BackColorHotMouse.Morph(source.BackColorFocused, 0.5f);
            Color color1, color2; float angle;
            PaintGetColors(color, PaintState.HotMouse | PaintState.Focused, out color1, out color2, out angle);
            PaintBackground(source.Bounds, graphics, 0, color1, color2, angle);
            PaintBorder(source, graphics, source.BorderColor, 0, 1, 1, System.Drawing.Drawing2D.DashStyle.Solid);
            PaintBorder(source, graphics, source.BorderColor, 2, 1, 1, System.Drawing.Drawing2D.DashStyle.Dot);
        }
        public static void PaintButtonFocused(IPaintBackground source, Graphics graphics)
        {
            Color color1, color2; float angle;
            PaintGetColors(source.BackColorFocused, PaintState.Pressed, out color1, out color2, out angle);
            PaintBackground(source.Bounds, graphics, 0, color1, color2, angle);
            PaintBorder(source, graphics, source.BorderColor, 0, 1, 1, System.Drawing.Drawing2D.DashStyle.Solid);
            PaintBorder(source, graphics, source.BorderColor, 2, 1, 1, System.Drawing.Drawing2D.DashStyle.Dot);
        }
        public static void PaintButtonHotMouse(IPaintBackground source, Graphics graphics)
        {
            Color color1, color2; float angle;
            PaintGetColors(source.BackColorHotMouse, PaintState.Pressed, out color1, out color2, out angle);
            PaintBackground(source.Bounds, graphics, 0, color1, color2, angle);
            PaintBorder(source, graphics, source.BorderColor, 0, 1, 0, System.Drawing.Drawing2D.DashStyle.Solid);
        }
        public static void PaintButtonStandard(IPaintBackground source, Graphics graphics)
        {
            Color color1, color2; float angle;
            PaintGetColors(source.BackColorStandard, PaintState.Pressed, out color1, out color2, out angle);
            PaintBackground(source.Bounds, graphics, 0, color1, color2, angle);
        }
        public static bool PaintGetColors(Color color, PaintState state, out Color color1, out Color color2, out float angle)
        {
            color1 = Color.Empty;
            color2 = Color.Empty;
            angle = 0f;

            if (state == PaintState.None) return false;
            bool isDisabled = ((state & PaintState.Disabled) != 0);
            bool isStandard = ((state & PaintState.Standard) != 0);
            bool isHotMouse = ((state & PaintState.HotMouse) != 0);
            bool isFocused = ((state & PaintState.Focused) != 0);
            bool isPressed = ((state & PaintState.Pressed) != 0);

            if (isDisabled)
            {
                color1 = color.Morph(Color.White, 0.25f);
                color2 = color.Morph(Color.DarkGray, 0.25f);
                angle = 90f;
            }
            else if (isPressed)
            {
                color1 = color.Morph(Color.White, 0.25f);
                color2 = color.Morph(Color.DarkGray, 0.25f);
                angle = -90f;
            }
            else if (isHotMouse && isFocused)
            {
                color1 = color.Morph(Color.White, 0.30f);
                color2 = color.Morph(Color.DarkGray, 0.35f);
                angle = 90f;
            }
            else if (isFocused)
            {
                color1 = color.Morph(Color.White, 0.30f);
                color2 = color.Morph(Color.DarkGray, 0.35f);
                angle = 90f;
            }
            else if (isHotMouse)
            {
                color1 = color.Morph(Color.White, 0.40f);
                color2 = color.Morph(Color.DarkGray, 0.20f);
                angle = 90f;
            }
            else
            {
                color1 = color.Morph(Color.White, 0.50f);
                color2 = color.Morph(Color.DarkGray, 0.10f);
                angle = 90f;
            }
            return true;
        }
        public static void PaintBackground(Rectangle bounds, Graphics graphics, int border, Color color, PaintState state)
        {
            Color color1, color2;
            float angle;
            if (!PaintGetColors(color, state, out color1, out color2, out angle))
                return;
            PaintBackground(bounds, graphics, border, color1, color2, angle);
        }
        public static void PaintBackground(Rectangle bounds, System.Drawing.Drawing2D.GraphicsPath shape, Graphics graphics, int border, Color color, PaintState state)
        {
            Color color1, color2;
            float angle;
            if (!PaintGetColors(color, state, out color1, out color2, out angle))
                return;
            PaintBackground(bounds, shape, graphics, border, color1, color2, angle);
        }
        public static void PaintBackground(Rectangle bounds, Graphics graphics, int border, Color color1, Color color2, float angle)
        {
            Rectangle area = new Rectangle(bounds.X + border, bounds.Y + border, bounds.Width - 2 * border, bounds.Height - 2 * border);
            using (System.Drawing.Drawing2D.LinearGradientBrush lgb = new System.Drawing.Drawing2D.LinearGradientBrush(area, color1, color2, angle))
            {
                graphics.FillRectangle(lgb, area);
            }
        }
        public static void PaintBackground(Rectangle bounds, System.Drawing.Drawing2D.GraphicsPath shape, Graphics graphics, int border, Color color1, Color color2, float angle)
        {
            Rectangle area = new Rectangle(bounds.X + border, bounds.Y + border, bounds.Width - 2 * border, bounds.Height - 2 * border);
            using (System.Drawing.Drawing2D.LinearGradientBrush lgb = new System.Drawing.Drawing2D.LinearGradientBrush(area, color1, color2, angle))
            {
                graphics.FillPath(lgb, shape);
                // graphics.FillRectangle(lgb, area);
            }
        }
        public static void PaintBorder(IPaintBackground source, Graphics graphics, Color color, int border, int sizeH, int sizeV, System.Drawing.Drawing2D.DashStyle dashStyle)
        {
            if (sizeH <= 0 && sizeV <= 0) return;

            int x0 = border;
            int x3 = source.Bounds.Width - border - 1;
            int y0 = border;
            int y3 = source.Bounds.Height - border - 1;
            using (Pen pen = new Pen(color, 1))
            {
                pen.DashStyle = dashStyle;
                // top line
                if (sizeH > 0)
                {
                    pen.Color = color.Morph(Color.White, 0.35f);
                    pen.Width = sizeH;
                    graphics.DrawLine(pen, x0, y0, x3, y0);
                }

                // left line
                if (sizeV > 0)
                {
                    pen.Width = sizeV;
                    graphics.DrawLine(pen, x0, y0, x0, y3);
                }

                // bottom line
                if (sizeH > 0)
                {
                    pen.Color = color.Morph(Color.Black, 0.50f);
                    pen.Width = sizeH;
                    graphics.DrawLine(pen, x0, y3, x3, y3);
                }

                // right line
                if (sizeV > 0)
                {
                    pen.Width = sizeV;
                    graphics.DrawLine(pen, x3, y0, x3, y3);
                }
            }
        }
        #endregion
        #region PaintLabel
        /// <summary>
        /// Vykreslí label
        /// </summary>
        /// <param name="source"></param>
        /// <param name="graphics"></param>
        public static void PaintLabel(IPaintLabel source, Graphics graphics)
        {
            PaintLabel(graphics, source.Text, source.Font, source.Bounds, source.LabelColor, source.ContentAlignment);
            PaintBorder(graphics, source.Bounds, source.BorderStyle);

        }
        /// <summary>
        /// Vykreslí label
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="text"></param>
        /// <param name="font"></param>
        /// <param name="bounds"></param>
        /// <param name="color"></param>
        /// <param name="alignment"></param>
        public static void PaintLabel(Graphics graphics, string text, Font font, Rectangle bounds, Color color, ContentAlignment alignment)
        {
            var state = graphics.Save();
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            StringFormat sf = new StringFormat(StringFormatFlags.LineLimit);
            SizeF size = graphics.MeasureString(text, font, bounds.Size, sf);
            RectangleF area = size.AlignTo(bounds, alignment);
            using (SolidBrush brush = new SolidBrush(color))
            {
                graphics.DrawString(text, font, brush, area, sf);
            }
            graphics.Restore(state);
        }
        #endregion
        #region PaintBorder
        /// <summary>
        /// Vykreslí rámeček
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="borderStyle"></param>
        public static void PaintBorder(Graphics graphics, Rectangle bounds, BorderStyle borderStyle)
        {
            Rectangle bb = new Rectangle(bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1);
            switch (borderStyle)
            {
                case BorderStyle.FixedSingle:
                    graphics.DrawRectangle(SystemPens.ControlDark, bb);
                    break;
                case BorderStyle.Fixed3D:
                    graphics.DrawLine(SystemPens.ControlDark, bb.Left, bb.Top, bb.Right, bb.Top);
                    graphics.DrawLine(SystemPens.ControlLight, bb.Right, bb.Top, bb.Right, bb.Bottom);
                    graphics.DrawLine(SystemPens.ControlLight, bb.Left, bb.Bottom, bb.Right, bb.Bottom);
                    graphics.DrawLine(SystemPens.ControlDark, bb.Left, bb.Top, bb.Left, bb.Bottom);
                    break;
            }
        }
        #endregion
        #region PaintTabFrame
        public static void PaintTabFrame(Graphics graphics, PaintTabFrameArgs args)
        {
            args.DetectDrawFrameBounds();

            if (args.Position == PaintTabFramePosition.None)
                args.PaintTabFrameDetectPosition();

            if (args.FramePath == null || args.TabPath == null)
                args.PaintTabFrameCreatePaths();

            PaintRectanglesGradient(graphics, args.DrawFrameBounds, args.EdgeWidth, args.ColorEdge, args.ColorInner);


            // Tab fill:
            float angle = 90f;
            if (args.TabBounds.Width > 0 && args.TabBounds.Height > 0)
            {
                double tang = 0.00d * (double)(args.TabBounds.Height) / (double)(args.TabBounds.Width);
                double atan = Math.Atan(tang);
                angle = 90f - (float)(180d / Math.PI * atan);
            }
            using (LinearGradientBrush lgt = new LinearGradientBrush(args.TabBounds, args.ColorTab, args.ColorEdge, angle))
            {
                // lgt.Blend = new Blend(3) { Factors = new float[] {0f, 0.333f, 1f}, Positions = new  }
                lgt.InterpolationColors = new ColorBlend(3) { Positions = new float[] { 0f, 0.25f, 1f }, Colors = new Color[] { args.ColorTab, args.ColorEdge, args.ColorEdge } };
                graphics.FillPath(lgt, args.TabPath);
            }

            // Frame+Tab border:
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            using (Pen penB = new Pen(args.ColorBorder, 1f))
            {
                graphics.DrawPath(penB, args.FramePath);
            }
        }
        /// <summary>
        /// Vykreslí sadu obdélníků počínaje daným obdélníkem, v dané šířce = počet obdélníků, s přechodem barev od color1 (na prvním obdélníku) k barvě color2 (na posledním).
        /// Zadaná šířka je kladná, pak se provádí kreslení dovnitř, anebo může být záporná = pak se obdélník rozšířuje ven.
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="area"></param>
        /// <param name="width"></param>
        /// <param name="color1"></param>
        /// <param name="color2"></param>
        public static void PaintRectanglesGradient(Graphics graphics, Rectangle area, int width, Color color1, Color color2)
        {
            int count = width;
            int step = 1;
            if (width == 0)
                return;
            if (width > 0)
            {
                int size = (area.Width < area.Height ? area.Width : area.Height);      // Menší z rozměrů Width, Height
                int maxw = size / 4;
                count = (width < maxw ? width : maxw);     // Počet obdélníků vykreslených dovnitř, nejvýše 1/4 plochy. Kladné číslo.
                step = -1;                                 // Změna velikosti obdélnéku po jednom kroku, zde se bude obdélník zmenšovat
            }
            else if (width < 0)
            {
                count = -width;                            // Počet obdélníků, kladné číslo
                step = +1;                                 // Změna velikosti obdélnéku po jednom kroku, zde se bude obdélník zvětšovat
            }

            Rectangle f = area;
            using (Pen pen = new Pen(Color.Black, 1f))
            {
                for (int s = 0; s < count; s++)
                {
                    float ratio = (s == 0 || count == 0 ? 0f : ((float)s) / ((float)(count - 1)));
                    pen.Color = color1.Morph(color2, ratio);
                    graphics.DrawRectangle(pen, f);
                    f.Inflate(step, step);
                }
            }
        }
        #endregion
        #region PaintFrame
        public static void PaintFrame(Graphics graphics, Rectangle bounds, Color colorB, int width, Color color1, Color color2, int roundCorner, string label)
        {
            Rectangle frame = bounds;
            frame.Width += 1;
            frame.Height += 1;

            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.InterpolationMode = InterpolationMode.Bicubic;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

            int size = (bounds.Width < bounds.Height ? bounds.Width : bounds.Height);      // Menší z rozměrů Width, Height
            int maxW = size / 4;
            int w = (width < maxW ? width : maxW);        // Šířka, nejvýše 1/4 plochy
            int r = w; // +roundCorner;
            Rectangle f = frame;
            for (int s = 0; s < w; s++)
            {
                GraphicsPath path = Asol.Reporting.Editor.Components.ShapeGenerator.GetRoundRectangle(f, r, r);
                float ratio = (s == 0 || w == 0 ? 0f : ((float)s) / ((float)w));
                Color c = (s == 0 ? colorB : color1.Morph(color2, ratio));
                using (Pen pen = new Pen(c, 1f))
                {
                    graphics.DrawPath(pen, path);
                }
                f.Inflate(-1, -1);
                r = (r > 0 ? r-- : 0);
            }

            {
                int s = 0;
                r = w; // +roundCorner;
                f = frame;
                GraphicsPath path = Asol.Reporting.Editor.Components.ShapeGenerator.GetRoundRectangle(f, r, r);
                float ratio = (s == 0 || w == 0 ? 0f : ((float)s) / ((float)w));
                Color c = (s == 0 ? colorB : color1.Morph(color2, ratio));
                using (Pen pen = new Pen(c, 1f))
                {
                    graphics.DrawPath(pen, path);
                }
            }
        }
        #endregion
        #region Vykreslení obrázku
        /// <summary>
        /// Vykreslí daný obrázek (image) do daného prostoru (bounds) tak, aby nebyl zkreslen poměr stran (image.Size), a aby se obrázek vešel do prostoru a nepřesahoval.
        /// V daném prostoru bude umístěn podle daného zarovnání (alignment).
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="image"></param>
        /// <param name="bounds"></param>
        /// <param name="alignment"></param>
        public static void PaintImage(Graphics graphics, Image image, Rectangle bounds, ContentAlignment alignment)
        {
            Size size = image.Size.ShrinkTo(bounds.Size, true);
            Rectangle area = size.AlignTo(bounds, alignment);
            graphics.DrawImage(image, area);
        }
        #endregion
        #region Set Graphics, restore previous state
        /// <summary>
        /// Prepare Graphics for smooth drawing (curve, text), quality smooth rendering.
        /// Return its original state, can be restored after drawing:  
        /// var state = Painter.GraphicsSetSmooth(graphics); any drawing...;  graphics.Restore(state);
        /// Restore of state is not requierd.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static GraphicsState GraphicsSetSmooth(Graphics graphics)
        {
            GraphicsState state = graphics.Save();
            _GraphicsSetSmooth(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for text drawing (lines, text), fast rendering lines and quality rendering of texts.
        /// Return its original state, can be restored after drawing:  
        /// var state = Painter.GraphicsSetSmooth(graphics); any drawing...;  graphics.Restore(state);
        /// Restore of state is not requierd.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static GraphicsState GraphicsSetText(Graphics graphics)
        {
            GraphicsState state = graphics.Save();
            _GraphicsSetText(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for sharp sharp (rectangles), fast rendering.
        /// Return its original state, can be restored after drawing:  
        /// var state = Painter.GraphicsSetSharp(graphics); any drawing...;  graphics.Restore(state);
        /// Restore of state is not requierd.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static GraphicsState GraphicsSetSharp(Graphics graphics)
        {
            GraphicsState state = graphics.Save();
            _GraphicsSetSharp(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for smooth drawing (curve, text), quality smooth rendering.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSmooth(graphics) { any drawing... }.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseSmooth(Graphics graphics)
        {
            IDisposable state = new GraphicsStateRestore(graphics);
            _GraphicsSetSmooth(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for smooth drawing (curve, text), quality smooth rendering.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSmooth(graphics) { any drawing... }.
        /// Set specified rectangle as Clip region to Graphics. On Dispose returns original Clip region.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseSmooth(Graphics graphics, Rectangle setClip)
        {
            IDisposable state = new GraphicsStateRestore(graphics, setClip);
            graphics.SetClip(setClip);
            _GraphicsSetSmooth(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for text drawing (lines, text), fast rendering lines and quality rendering of texts.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSmooth(graphics) { any drawing... }.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseText(Graphics graphics)
        {
            IDisposable state = new GraphicsStateRestore(graphics);
            _GraphicsSetText(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for text drawing (lines, text), fast rendering lines and quality rendering of texts.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSmooth(graphics) { any drawing... }.
        /// Set specified rectangle as Clip region to Graphics. On Dispose returns original Clip region.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseText(Graphics graphics, Rectangle setClip)
        {
            IDisposable state = new GraphicsStateRestore(graphics, setClip);
            graphics.SetClip(setClip);
            _GraphicsSetText(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for sharp sharp (rectangles), fast rendering.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSharp(graphics) { any drawing... }.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseSharp(Graphics graphics)
        {
            IDisposable state = new GraphicsStateRestore(graphics);
            _GraphicsSetSharp(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for sharp sharp (rectangles), fast rendering.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSharp(graphics) { any drawing... }.
        /// Set specified rectangle as Clip region to Graphics. On Dispose returns original Clip region.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseSharp(Graphics graphics, Rectangle setClip)
        {
            IDisposable state = new GraphicsStateRestore(graphics, setClip);
            graphics.SetClip(setClip);
            _GraphicsSetSharp(graphics);
            return state;
        }
        /// <summary>
        /// Set graphic to smooth mode
        /// </summary>
        /// <param name="graphics"></param>
        private static void _GraphicsSetSmooth(Graphics graphics)
        {
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
        }
        /// <summary>
        /// Set graphic to text mode
        /// </summary>
        /// <param name="graphics"></param>
        private static void _GraphicsSetText(Graphics graphics)
        {
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
        }
        /// <summary>
        /// Set graphic to sharp mode
        /// </summary>
        /// <param name="graphics"></param>
        private static void _GraphicsSetSharp(Graphics graphics)
        {
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;
        }
        #region class GraphicsStateRestore : Disposable pattern for graphics.Save(), Set, use and Restore on Dispose
        /// <summary>
        /// GraphicsStateRestore : Disposable pattern for graphics.Save(), set, use and Restore on Dispose
        /// </summary>
        private class GraphicsStateRestore : IDisposable
        {
            public GraphicsStateRestore(Graphics graphics)
            {
                this._Graphics = graphics;
                this._State = graphics.Save();
            }
            public GraphicsStateRestore(Graphics graphics, Rectangle setClip)
            {
                this._Graphics = graphics;
                this._State = graphics.Save();
                this._Clip = setClip;
                this._OriginClip = graphics.Clip;
            }
            /// <summary>
            /// Graphics (via WeakReference)
            /// </summary>
            private Graphics _Graphics
            {
                set
                {
                    this.__Graphics = (value == null ? null : new WeakReference(value));
                }
                get
                {
                    if (!this._HasGraphics == null) return null;
                    return this.__Graphics.Target as Graphics;
                }
            }
            /// <summary>
            /// true when WeakReference to Graphics is valid
            /// </summary>
            private bool _HasGraphics
            {
                get { return (this.__Graphics != null && this.__Graphics.IsAlive && this.__Graphics.Target is Graphics); }
            }
            private WeakReference __Graphics;
            private GraphicsState _State;
            private Rectangle? _Clip;
            private Region _OriginClip;
            void IDisposable.Dispose()
            {
                if (this._State != null && this._HasGraphics)
                {
                    Graphics graphics = this._Graphics;
                    graphics.Restore(this._State);
                    if (this._Clip.HasValue)
                    {
                        if (this._OriginClip != null)
                            graphics.SetClip(this._OriginClip, CombineMode.Replace);
                        else
                            graphics.ResetClip();
                    }
                }
                this._Graphics = null;
                this._State = null;
                this._OriginClip = null;
                this._Clip = null;
            }
        }
        #endregion
        #endregion
        #region Static property, containing standardised graphic tools
        public static Brush InteractiveClipBrushForState(GInteractiveState state)
        {
            switch (state)
            {
                case GInteractiveState.None: return InteractiveClipStandardBrush;
                case GInteractiveState.Mouse: return InteractiveClipMouseBrush;
                case GInteractiveState.LeftDown: return InteractiveClipDownBrush;
                case GInteractiveState.RightDown: return InteractiveClipDownBrush;
                case GInteractiveState.LeftDrag: return InteractiveClipDragBrush;
                case GInteractiveState.RightDrag: return InteractiveClipDragBrush;
            }
            return InteractiveClipStandardBrush;
        }
        public static Brush InteractiveClipStandardBrush
        {
            get
            {
                if (_InteractiveClipStandardBrush == null)
                    _InteractiveClipStandardBrush = new SolidBrush(InteractiveClipStandardColor);
                return _InteractiveClipStandardBrush;
            }
        } private static Brush _InteractiveClipStandardBrush;
        public static Brush InteractiveClipMouseBrush
        {
            get
            {
                if (_InteractiveClipMouseBrush == null)
                    _InteractiveClipMouseBrush = new SolidBrush(InteractiveClipMouseColor);
                return _InteractiveClipMouseBrush;
            }
        } private static Brush _InteractiveClipMouseBrush;
        public static Brush InteractiveClipDownBrush
        {
            get
            {
                if (_InteractiveClipDownBrush == null)
                    _InteractiveClipDownBrush = new SolidBrush(InteractiveClipDownColor);
                return _InteractiveClipDownBrush;
            }
        } private static Brush _InteractiveClipDownBrush;
        public static Brush InteractiveClipDragBrush
        {
            get
            {
                if (_InteractiveClipDragBrush == null)
                    _InteractiveClipDragBrush = new SolidBrush(InteractiveClipDragColor);
                return _InteractiveClipDragBrush;
            }
        } private static Brush _InteractiveClipDragBrush;
        public static Color InteractiveClipStandardColor { get { return Color.DimGray; } }
        public static Color InteractiveClipMouseColor { get { return Color.BlueViolet; } }
        public static Color InteractiveClipDownColor { get { return Color.Black; } }
        public static Color InteractiveClipDragColor { get { return Color.Blue; } }
        #endregion

    }
    #region class PaintTabFrameArgs + enum PaintTabFramePosition = Data pro kreslení Frame + Tab
    /// <summary>
    /// Data pro kreslení Frame + Tab
    /// </summary>
    public class PaintTabFrameArgs
    {
        public PaintTabFrameArgs()
        {
            this.TabCornerSize = 2;
            this.ColorBorder = Color.FromArgb(216, Color.Black);
            this.ColorTab = Color.FromArgb(216, Color.Cornsilk);
            this.ColorEdge = Color.FromArgb(216, Color.Cornsilk);
            this.EdgeWidth = 4;
            this.ColorInner = Color.FromArgb(0, 255, 255, 255);
            this.Position = PaintTabFramePosition.None;
            this.FramePath = null;
            this.TabPath = null;
        }
        /// <summary>
        /// Prostor frame. Běžně se frame vykresluje dovnitř (ale TAB se vykresluje i vně), tzn. na souřadnici Right a Bottom se NEVYSKYTUJE žádný pixel na který by se tento FRAME kreslil.
        /// Tedy kreslí se na šířku a výšku o 1px menší (tento upravený prostor je uložen do DrawFrameBounds).
        /// </summary>
        public Rectangle FrameBounds { get { return this._FrameBounds; } set { this._FrameBounds = value; this._FrameBoundsIsExact = false; this._Position = PaintTabFramePosition.None; this._FramePath = null; this._TabPath = null; } } private Rectangle _FrameBounds;
        /// <summary>
        /// true, pokud chceme kreslit i na pixel Right a Bottom = nezmenšovat FrameBounds o 1 px.
        /// </summary>
        public bool FrameBoundsIsExact { get { return this._FrameBoundsIsExact; } set { this._FrameBoundsIsExact = value; } } private bool _FrameBoundsIsExact;
        /// <summary>
        /// Souřadnice TABu. Před vykreslením jsou korigovány tak, aby dobře navazovaly na FrameBounds.
        /// Pokud tato korekce není žádoucí, je třeba po nastavení TabBounds nastavit TabBoundsIsExact = true.
        /// </summary>
        public Rectangle TabBounds { get { return this._TabBounds; } set { this._TabBounds = value; this._TabBoundsIsExact = false; this._Position = PaintTabFramePosition.None; this._FramePath = null; this._TabPath = null; } } private Rectangle _TabBounds;
        /// <summary>
        /// true, pokud nechceme korigovat TabBounds, ale chceme TAB kreslit přesně tam, kde se nachází.
        /// </summary>
        public bool TabBoundsIsExact { get { return this._TabBoundsIsExact; } set { this._TabBoundsIsExact = value; } } private bool _TabBoundsIsExact;
        /// <summary>
        /// Velikost "rohu" na Tabu.
        /// Default je 2, optimální je 2 až 4, akceptovaná hodnota je 0 až 7 včetně.
        /// </summary>
        public int TabCornerSize { get { return this._TabCornerSize; } set { this._TabCornerSize = (value < 0 ? 0 : (value > 7 ? 7 : value)); this.FramePath = null; this._FramePath = null; this._TabPath = null; } } private int _TabCornerSize;
        public string Label { get; set; }
        /// <summary>
        /// Barva okrajové linky kolem FRAME + TAB
        /// </summary>
        public Color ColorBorder { get; set; }
        /// <summary>
        /// Barva vrcholku TABu (může signalizovat aktivitu myši)
        /// </summary>
        public Color ColorTab { get; set; }
        /// <summary>
        /// Barva výplně na vnějším okraji, současně barva větší části TABu
        /// </summary>
        public Color ColorEdge { get; set; }
        /// <summary>
        /// Šířka Frame dovnitř.
        /// Default je 4, optimální je 3 až 7, akceptovaná hodnota je 0 až 20 včetně.
        /// Pozor, pokud barva ColorInner je zcela transparentní, tak poslední pixel v této šířce nebude vidět.
        /// Pokud bude zadáno EdgeWidth = 0, pak bude vykreslen jen rámeček barvou ColorBorder.
        /// </summary>
        public int EdgeWidth { get { return this._EdgeWidth; } set { this._EdgeWidth = (value < 0 ? 0 : (value > 20 ? 20 : value)); } } private int _EdgeWidth;
        /// <summary>
        /// Barva FRAME koncová = uvnitř frame. Tato barva je použita na poslední pixel kresby.
        /// </summary>
        public Color ColorInner { get; set; }
        /// <summary>
        /// Souřadnice Frame na které se reálně kreslí. Vycházejí z FrameBounds, ale bývají o 1px menší.
        /// </summary>
        public Rectangle DrawFrameBounds { get { return this._DrawFrameBounds; } set { this._DrawFrameBounds = value; this._Position = PaintTabFramePosition.None; this._FramePath = null; this._TabPath = null; } } private Rectangle _DrawFrameBounds;
        /// <summary>
        /// Pozice TABU vzhledem k FRAME.
        /// Po vložíení souřadnic je None, v průběhu Paint je detekována. Pokud je před Paintem nastavena, je akceptována (i kdyby byla blbě).
        /// </summary>
        public PaintTabFramePosition Position { get { return this._Position; } set { this._Position = value; } } private PaintTabFramePosition _Position;
        /// <summary>
        /// Souřadnice bodů okraje (FRAME + TAB). Po vložení souřadnic je NULL, v procesu Paint je vypočteno.
        /// Pokud je před Paint něčím naplněno, je to akceptováno.
        /// </summary>
        public GraphicsPath FramePath { get { return this._FramePath; } set { this._FramePath = value; } } private GraphicsPath _FramePath;
        /// <summary>
        /// Souřadnice bodů TABu. Po vložení souřadnic je NULL, v procesu Paint je vypočteno.
        /// Pokud je před Paint něčím naplněno, je to akceptováno.
        /// </summary>
        public GraphicsPath TabPath { get { return this._TabPath; } set { this._TabPath = value; } } private GraphicsPath _TabPath;

        #region Podpora pro převody souřadnic FRAME a TAB na grafické cesty
        /// <summary>
        /// Zmenší velikost FrameBounds o 1,1 tak, aby kreslené okraje (Draw) vedly po okrajích zadaného prostoru.
        /// Pokud FrameBounds je například (10,10,40,20), tak očekáváme, že vykreslený Frame bude mít šířku 40 a výšku 20.
        /// Bez tohoto zmenšení by okraje byly vykresleny do souřadnic X: 10, 50 (50=10+40), ale tím by frame zabral 41 pixelů na šířku.
        /// Naproti tomu Fill by vyplnil skutečně jen plochu (10 až 49), tedy 40 pixelů.
        /// </summary>
        public void DetectDrawFrameBounds()
        {
            Rectangle frame = this.FrameBounds;
            if (!this.FrameBoundsIsExact)
            {
                frame.Width--;
                frame.Height--;
            }
            this.DrawFrameBounds = frame;
        }
        /// <summary>
        /// Určí pozici TABu (TabBounds) proti pozici základního Rectangle (FrameBounds).
        /// Zjištěnou pozici vloží do this.Position.
        /// Korigovanou pozici TABu vloží do this.TabBounds, pokud není nastaveno this.TabBoundsIsExact = true.
        /// </summary>
        /// <param name="frameBounds"></param>
        /// <param name="tabBounds"></param>
        /// <returns></returns>
        public void PaintTabFrameDetectPosition()
        {
            Rectangle fb = this.DrawFrameBounds;
            Rectangle tb = this.TabBounds;
            PaintTabFramePosition position = PaintTabFramePosition.None;
            Point tabCenter = tb.Center();
            Size tabSize = tb.Size;
            int tabX = tb.X;
            int tabY = tb.Y;

            // Vztah středu TABu proti každé hraně FRAME:
            int xcl = tabCenter.X - fb.X;             // Střed TABu X proti levé hraně 
            int xcr = tabCenter.X - fb.Right;         // Střed TABu X proti pravé hraně
            int yct = tabCenter.Y - fb.Y;             // Střed TABu Y proti horní hraně
            int ycb = tabCenter.Y - fb.Bottom;        // Střed TABu Y proti dolní hraně

            // Vzdálenost hrany TABu od hrany FRAME, z pohledu kdy TAB je uvnitř FRAME (pokud TAB je částečně venku, pak hodnota bude záporná):
            int xll = tb.X - fb.X;
            int xrr = fb.Right - tb.Right;
            int ytt = tb.Y - fb.Y;
            int ybb = fb.Bottom - tb.Bottom;

            bool setX = false;
            bool setY = false;
            if (yct < 0)
            {   // TAB je víceméně nahoře od horní hrany FRAME:
                position = PaintTabFramePosition.OnTopEdge;
                tabY = fb.Y - tabSize.Height;
                setX = true;
            }
            else if (ycb > 0)
            {   // TAB je víceméně dole od dolní hrany FRAME:
                position = PaintTabFramePosition.UnderBottomEdge;
                tabY = fb.Bottom;
                setX = true;
            }
            else if (xcl < 0)
            {   // TAB je víceméně vlevo od levé hrany FRAME:
                position = PaintTabFramePosition.BeforeLeftEdge;
                tabX = fb.X - tabSize.Width;
                setY = true;
            }
            else if (xcr > 0)
            {   // TAB je víceméně vpravo od pravé hrany FRAME:
                position = PaintTabFramePosition.AfterRightEdge;
                tabX = fb.Right;
                setY = true;
            }
            else if (_IsSmalest(xll, xrr, ytt, ybb))
            {   // TAB je uvnitř, a nejblíže k levé hraně FRAME:
                position = PaintTabFramePosition.AfterLeftEdge;
                tabX = fb.X;
                setY = true;
            }
            else if (_IsSmalest(xrr, xll, ytt, ybb))
            {   // TAB je uvnitř, a nejblíže k pravé hraně FRAME:
                position = PaintTabFramePosition.BeforeRightEdge;
                tabX = fb.Right - tabSize.Width;
                setY = true;
            }
            else if (_IsSmalest(ytt, xll, xrr, ybb))
            {   // TAB je uvnitř, a nejblíže k horní hraně FRAME:
                position = PaintTabFramePosition.UnderTopEdge;
                tabY = fb.Y;
                setX = true;
            }
            else if (_IsSmalest(ybb, xll, xrr, ytt))
            {   // TAB je uvnitř, a nejblíže k dolní hraně FRAME:
                position = PaintTabFramePosition.AfterRightEdge;
                tabY = fb.Bottom - tabSize.Height;
                setX = true;
            }

            if (setX)
                tabX = (tabX < fb.X ? fb.X : (tb.Right > fb.Right ? fb.Right - tabSize.Width : tabX));
            if (setY)
                tabY = (tabY < fb.Y ? fb.Y : (tb.Bottom > fb.Bottom ? fb.Bottom - tabSize.Height : tabY));

            if (!this.TabBoundsIsExact)
                this.TabBounds = new Rectangle(new Point(tabX, tabY), tabSize);          // Instanční logika nám nastaví _TabBoundsIsExact = false; _Position = None; _FramePath = null; _TabPath = null
            this._Position = position;                                                   // Vkládáme pro jistotu bez instanční logiky
        }
        /// <summary>
        /// Pro aktuální souřadnice FrameBounds a TabBounds a při dané pozici TABu Position vytvoří grafické cesty pro border (FramePath) a pro výplň TABu (TabPath)
        /// </summary>
        public void PaintTabFrameCreatePaths()
        {
            #region Vytvořím vnější body TABu (podle jeho pozice) do listu tabPoints; pořadí bodů je ve směru vykreslování celého Frame (od LeftTop přes RightTop a RightBottom až k LeftBottom a zpátky k LeftTop)
            Rectangle fb = this.DrawFrameBounds;
            Rectangle tb = this.TabBounds;
            int tc = this.TabCornerSize;
            int fx0 = fb.X;
            int fx9 = fb.Right;
            int fy0 = fb.Y;
            int fy9 = fb.Bottom;
            int tx0 = tb.X;
            int tx1 = tb.X + tc;
            int tx9 = tb.Right;
            int tx8 = tx9 - tc;
            int ty0 = tb.Y;
            int ty1 = ty0 + tc;
            int ty9 = tb.Bottom;
            int ty8 = ty9 - tc;

            List<Point> tabPoints = new List<Point>();
            int edge = 0;
            bool inner = false;

            switch (this.Position)
            {
                case PaintTabFramePosition.OnTopEdge:
                    tabPoints.Add(new Point(tx0, ty9));
                    tabPoints.Add(new Point(tx0, ty1));
                    tabPoints.Add(new Point(tx1, ty0));
                    tabPoints.Add(new Point(tx8, ty0));
                    tabPoints.Add(new Point(tx9, ty1));
                    tabPoints.Add(new Point(tx9, ty9));
                    edge = 1;
                    inner = false;
                    break;
                case PaintTabFramePosition.UnderTopEdge:
                    tabPoints.Add(new Point(tx0, ty0));
                    tabPoints.Add(new Point(tx0, ty8));
                    tabPoints.Add(new Point(tx1, ty9));
                    tabPoints.Add(new Point(tx8, ty9));
                    tabPoints.Add(new Point(tx9, ty8));
                    tabPoints.Add(new Point(tx9, ty0));
                    edge = 1;
                    inner = true;
                    break;

                case PaintTabFramePosition.AfterRightEdge:
                    tabPoints.Add(new Point(tx0, ty0));
                    tabPoints.Add(new Point(tx8, ty0));
                    tabPoints.Add(new Point(tx9, ty1));
                    tabPoints.Add(new Point(tx9, ty8));
                    tabPoints.Add(new Point(tx8, ty9));
                    tabPoints.Add(new Point(tx0, ty9));
                    edge = 2;
                    inner = false;
                    break;
                case PaintTabFramePosition.BeforeRightEdge:
                    tabPoints.Add(new Point(tx9, ty0));
                    tabPoints.Add(new Point(tx1, ty0));
                    tabPoints.Add(new Point(tx0, ty1));
                    tabPoints.Add(new Point(tx0, ty8));
                    tabPoints.Add(new Point(tx1, ty9));
                    tabPoints.Add(new Point(tx9, ty9));
                    edge = 2;
                    inner = true;
                    break;

                case PaintTabFramePosition.UnderBottomEdge:
                    tabPoints.Add(new Point(tx9, ty0));
                    tabPoints.Add(new Point(tx9, ty8));
                    tabPoints.Add(new Point(tx8, ty9));
                    tabPoints.Add(new Point(tx1, ty9));
                    tabPoints.Add(new Point(tx0, ty8));
                    tabPoints.Add(new Point(tx0, ty0));
                    edge = 3;
                    inner = false;
                    break;
                case PaintTabFramePosition.OnBottomEdge:
                    tabPoints.Add(new Point(tx9, ty9));
                    tabPoints.Add(new Point(tx9, ty1));
                    tabPoints.Add(new Point(tx8, ty0));
                    tabPoints.Add(new Point(tx1, ty0));
                    tabPoints.Add(new Point(tx0, ty1));
                    tabPoints.Add(new Point(tx0, ty9));
                    edge = 3;
                    inner = true;
                    break;

                case PaintTabFramePosition.BeforeLeftEdge:
                    tabPoints.Add(new Point(tx9, ty9));
                    tabPoints.Add(new Point(tx1, ty9));
                    tabPoints.Add(new Point(tx0, ty8));
                    tabPoints.Add(new Point(tx0, ty1));
                    tabPoints.Add(new Point(tx1, ty0));
                    tabPoints.Add(new Point(tx9, ty0));
                    edge = 4;
                    inner = false;
                    break;
                case PaintTabFramePosition.AfterLeftEdge:
                    tabPoints.Add(new Point(tx0, ty9));
                    tabPoints.Add(new Point(tx8, ty9));
                    tabPoints.Add(new Point(tx9, ty8));
                    tabPoints.Add(new Point(tx9, ty1));
                    tabPoints.Add(new Point(tx8, ty0));
                    tabPoints.Add(new Point(tx0, ty0));
                    edge = 4;
                    inner = true;
                    break;
            }
            #endregion

            // Seznam bodů FRAME (=krajní rohy + do vhodného místa zařadit TAB):
            List<Point> framePoints = new List<Point>();

            framePoints.Add(new Point(fx0, fy0));
            if (edge == 1)
                framePoints.AddRange(tabPoints);

            framePoints.Add(new Point(fx9, fy0));
            if (edge == 2)
                framePoints.AddRange(tabPoints);

            framePoints.Add(new Point(fx9, fy9));
            if (edge == 3)
                framePoints.AddRange(tabPoints);

            framePoints.Add(new Point(fx0, fy9));
            if (edge == 4)
                framePoints.AddRange(tabPoints);

            framePoints.Add(framePoints[0]);

            // Sestavím GraphicsPath pro FRAME:
            if (this.FramePath == null)
            {
                this.FramePath = new GraphicsPath();
                this.FramePath.AddPolygon(framePoints.ToArray());
                this.FramePath.CloseFigure();
                if (inner)
                    this.FramePath.AddLine(tabPoints[0], tabPoints[tabPoints.Count - 1]);
            }

            // Sestavím GraphicsPath pro TAB:
            if (this.TabPath == null)
            {
                this.TabPath = new GraphicsPath();
                if (tabPoints.Count > 0)
                {   // Standardní cesta:
                    tabPoints.Add(tabPoints[0]);                // tabPoints má mít na poslední pozici bod shodný s prvním bodem
                    this.TabPath.AddPolygon(tabPoints.ToArray());
                    this.TabPath.CloseFigure();
                }
                else
                {   // Nouzová cesta, pokud Position bude None:
                    this.TabPath.AddRectangle(this.TabBounds);
                }
            }
        }
        /// <summary>
        /// Metoda vrátí true v případě, že první předaná hodnota je nejmenší ze všech předaných hodnot.
        /// Pokud se mezi hodnotami 2 ÷ nn najde nějaká, která je menší než první, pak vrací false.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="others"></param>
        /// <returns></returns>
        private static bool _IsSmalest(int value, params int[] others)
        {
            foreach (int other in others)
            {
                if (value > other) return false;
            }
            return true;
        }
        #endregion
    }
    /// <summary>
    /// Pozice Tabu vzhledem k Frame
    /// </summary>
    public enum PaintTabFramePosition
    {
        None = 0,
        OnTopEdge,
        UnderTopEdge,
        BeforeRightEdge,
        AfterRightEdge,
        OnBottomEdge,
        UnderBottomEdge,
        BeforeLeftEdge,
        AfterLeftEdge
    }
    #endregion
    #region interface IPaintBackground + IPaintLabel, enum PaintState+PaintMode
    /// <summary>
    /// Interface předepisující vlastnosti objektu nutné k vykreslení pozadí
    /// </summary>
    public interface IPaintBackground
    {
        PaintState State { get; }
        PaintMode PaintMode { get; }
        Rectangle Bounds { get; }
        Color BackColorDisabled { get; }
        Color BackColorStandard { get; }
        Color BackColorHotMouse { get; }
        Color BackColorFocused { get; }
        Color BackColorPressed { get; }
        Color BorderColor { get; }
    }
    /// <summary>
    /// Interface předepisující vlastnosti objektu nutné k vykreslení labelu a borderu
    /// </summary>
    public interface IPaintLabel
    {
        Rectangle Bounds { get; }
        int Shift { get; }
        ContentAlignment ContentAlignment { get; }
        string Text { get; }
        Font Font { get; }
        Color LabelColor { get; }
        BorderStyle BorderStyle { get; }
    }
    [Flags]
    public enum PaintState
    {
        None = 0,
        Disabled = 0x01,
        Standard = 0x02,
        HotMouse = 0x04,
        Focused = 0x08,
        Pressed = 0x10
    }
    public enum PaintMode
    {
        None = 0,
        FlatColor,
        GradientHorizontal,
        GradientVertical
    }
    #endregion
}
